#include "sys.h"
#include "cJSON.h"
#include "libfyaml.h"
#include "local_msg.h"
#include "sys_log.h"
#include "thread_msg.h"
#include <pthread.h>
#include <dirent.h>
#include <fcntl.h>
#include <main.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysinfo.h>
#include <sys/types.h>

sys_status_t sys_stat;
app_launch_list_t app_launch;

/**
 * Sys git app pid
 */
int pid_get(char *app_name)
{
    DIR *dir;
    struct dirent *dp;
    int app_len, pid, len, find_len;
    char exe[300], path[300];
    char *str;
    const char *base_dir = "/proc";

    app_len = strlen(app_name);

    dir = opendir(base_dir);
    if (dir == NULL)
    {
        perror("opendir error");
        return -1;
    }

    while ((dp = readdir(dir)) != NULL)
    {
        if ((pid = atoi(dp->d_name)) == 0)
            continue;
        snprintf(exe, 300, "/proc/%s/exe", dp->d_name);
        if ((len = readlink(exe, path, 299)) < 0)
            continue;
        path[len] = '\0';
        str = strrchr(path, '/');
        if (str == NULL)
            continue;
        str++;
        find_len = strlen(str);
        if (find_len < app_len)
            continue;
        if (!strncmp(app_name, str, app_len))
        {
            /* to avoid subname like search proc tao but proc taolinke matched */
            if (str[app_len] == ' ' || str[app_len] == '\0')
            {
                closedir(dir);
                return pid;
            }
        }
    }
    closedir(dir);
    return -1;
}

/**
 * Sys check dir 
 */
int is_dir_exist(const char *dir_path)
{
    DIR *dirptr = NULL;

    if (dir_path == NULL)
        return -1;

    dirptr = opendir(dir_path);
    if (dirptr == NULL)
    {
        return -1;
    }
    closedir(dirptr);
    return 0;
}

/**
 * Sys restart app
 */
int sys_app_restart(int idx)
{
    int pid;
    char command[200];

    // kill pid
    memset(command, 0, sizeof(command));
    snprintf(command, 200, "%s", app_launch.app_list[idx].app);
    sys_log_info("app name : %s\n", command);
    pid = pid_get(command);
    if (pid != -1) {
        sys_log_info("app name : %s\n", command);
        memset(command, 0, sizeof(command));
        sprintf(command, "kill -9 %d", pid);
        system(command);
    }

    // run app
    if (app_launch.app_list[idx].cmd) 
        snprintf(command, 200, "%s %s %s", app_launch.app_list[idx].file, app_launch.app_list[idx].cmd, "&");
    else
        snprintf(command, 200, "%s %s", app_launch.app_list[idx].file, "&");
    system(command);
    return 0;
}

/**
 * Get the current status of the APP
 */
int sys_get_process_stat(char *name)
{
    int pid, proc_stat;
    char command[200];

    memset(command, 0, sizeof(command));
    snprintf(command, 200, "%s", name);
    sys_log_info("app name : %s\n", command);
    pid = pid_get(command);
    if (pid != -1)
        proc_stat = 1;
    else
        proc_stat = 0;

    return proc_stat;
}

/**
 * Sys new status check
 */
int sys_app_stat_check(void)
{
    for (int i = 0; i < app_launch.app_cnt; i++) 
    {
        if (strcmp(sys_stat.app[i].name, "") == 0) {
            if (strcmp(app_launch.app_list[i].app, "") != 0) {
                sys_log_info("app %s need start\n", app_launch.app_list[i].app);
                strcpy(sys_stat.app[i].name, app_launch.app_list[i].app);
            } else {
                continue;
            }
        }

        sys_stat.app[i].new_stat = sys_get_process_stat(sys_stat.app[i].name);
        if (sys_stat.app[i].new_stat == 0) {
            if (sys_stat.app[i].rest_cnt < 5) { // wait @50s
                sys_app_restart(i);
                sys_stat.app[i].rest_cnt++;
                sys_stat.app[i].new_stat = 1;
            } else {
                sys_log_erro("app %s run error\n", sys_stat.app[i].name);
                if (sys_stat.app[i].err_cnt++ > 10) {
                    sys_stat.app[i].err_cnt = 0;
                    sys_stat.app[i].rest_cnt = 0;
                }
            }
        }
        
        if (sys_stat.app[i].new_stat != sys_stat.app[i].old_stat) {
            sys_stat.app[i].old_stat = sys_stat.app[i].new_stat;
            if (sys_stat.app[i].new_stat == 1) {
                local_mqtt_app_init(sys_stat.app[i].name);
            }
        }
    }
    return 0;
}

/**
 * Read app launch list
 */
int app_launch_list_read(char *path)
{
    int i;
    int count;
    size_t size_len;
    int str_size;
    char applist_path[500];
    char path_name[200];
    char command[200];
    char *version = NULL;
    const char *scalar_name = NULL;
    
    snprintf(applist_path, 500, "%s%s%s", path, APP_HOME, "file/app_launch.yaml");
    fyd = fy_document_build_from_file(NULL, applist_path);
    if (!fyd) {
        // Not exit
        sys_log_erro("applist_path(%s) not exist\n", applist_path);

        // Reset version
        strncpy(app_launch.ver, "0.1", 10);
        sys_log_info("sys current version : %s\n", "v0.1");
        return -1;
    }

    fyn_root = fy_document_root(fyd);
    fyn_ver = fy_node_by_path(fyn_root, "/version", FY_NT, FYNWF_DONT_FOLLOW);
    scalar_name = fy_node_get_scalar(fyn_ver, &size_len);
    sys_log_info("current version : %s\n", scalar_name);
    version = strchr(scalar_name, 'v');
    version++;
    strncpy(app_launch.ver, version, 10);

    fyn_files = fy_node_by_path(fyn_root, "/files", FY_NT, FYNWF_DONT_FOLLOW);
    count = fy_node_sequence_item_count(fyn_files);
    app_launch.app_cnt = 0;

    for (i = 0; i < count; i++) {
        memset(path_name, 0, sizeof(path_name));
        snprintf(path_name, 200, "/files/[%d]/app", i);
        fyn_app = fy_node_by_path(fyn_root, path_name, FY_NT, FYNWF_DONT_FOLLOW);
        if (fyn_app != NULL) {
            // app
            fyn_app = fy_node_by_path(fyn_root, path_name, FY_NT, FYNWF_DONT_FOLLOW);
            scalar_name = fy_node_get_scalar(fyn_app, &size_len);
            strncpy(app_launch.app_list[app_launch.app_cnt].app, scalar_name, size_len);

            // file
            memset(path_name, 0, sizeof(path_name));
            snprintf(path_name, 200, "/files/[%d]/file", i);
            fyn_file = fy_node_by_path(fyn_root, path_name, FY_NT, FYNWF_DONT_FOLLOW);
            scalar_name = fy_node_get_scalar(fyn_file, &size_len);
            strncpy(app_launch.app_list[app_launch.app_cnt].file, scalar_name, size_len);

            // cmd
            memset(path_name, 0, sizeof(path_name));
            snprintf(path_name, 200, "/files/[%d]/launch/cmd", i);
            fyn_cmd = fy_node_by_path(fyn_root, path_name, FY_NT, FYNWF_DONT_FOLLOW);
            scalar_name = fy_node_get_scalar(fyn_cmd, &size_len);
            strncpy(app_launch.app_list[app_launch.app_cnt].cmd, scalar_name, size_len);
            app_launch.app_cnt++;
        }
    }

    fy_document_destroy(fyd);
    return 0;
}

/**
 * Create work path
 */
void app_work_path_create(char *path)
{
    int ret = 0;
    char work_path[500];

    snprintf(work_path, 500, "%s%s", path, "sys");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "sys/sys_agent");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "sys/sys_agent/log");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "sys/sys_agent/profile");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "sys/sys_agent/profile/canopen");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "sys/sys_agent/profile/modbus_485");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "sys/sys_agent/profile/modbus_usb");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "sys/sys_manager");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "sys/sys_manager/file");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "sys/sys_web");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "sys/sys_web/html");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "cloud");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "cloud/cloud_ali");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "cloud/cloud_chint");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "dev");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "dev/dev_canopen");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "dev/dev_modbus_485");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "dev/dev_modbus_usb");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "mid");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "mid/mid_sql");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }

    memset(work_path, 0, sizeof(work_path));
    snprintf(work_path, 500, "%s%s", path, "ui");
    if (is_dir_exist(work_path))
    {
        ret = mkdir(work_path, 0755);
        if (ret == -1)
        {
            sys_log_erro("path:(%s) create err\n", work_path);
        }
    }
}

/**
 * Sys local mqtt init
 */
int sys_local_mqtt_init(void)
{
    int ret = -1;
	ret = local_mqtt_init();
	if (ret != 0) {
		sys_log_erro("local mqtt init error\n");
		return -1;
	}

	ret = local_msg_init();
	if (ret != 0) {
		sys_log_erro("local msg init error\n");
		return -1;
	}
    return 0;
}

/**
 * Sys thread msg init
 */
int sys_thread_msg_init(char *path)
{
    int ret = -1;
    ret = thread_msg_init(path);
	if (ret != 0) {
		sys_log_erro("thread msg init error\n");
		return -1;
	}
    return 0;
}

/**
 * Sys work init
 */
int sys_work_init(char *path)
{
    app_work_path_create(path);

    app_launch_list_read(path);
    return 0;
}

/**
 * Sys check
 */
int sys_mangers_check(void)
{
    // app
    sys_app_stat_check();

    return 0;
}

void *pthread_sys_check(void* param)
{
    int res = 0;
    while (1) {
        sys_log_info("pthread_sys_check\n");
        res = sys_mangers_check();
        if (res < 0) {
            sys_log_erro("pthread sys check app fail\n");
        }
        sleep(1);
    }
    return NULL;
}

int sys_init(char *path)
{
    int ret = -1;
    pthread_t thread_id_sys_check;

    ret = sys_work_init(path);
    if (ret != 0) {
        sys_log_erro("sys work init error\n");
        return -1;
	}

    ret = sys_thread_msg_init(path);
	if (ret != 0) {
		sys_log_erro("sys thread msg init error\n");
		return -1;
	}

    ret = sys_local_mqtt_init();
	if (ret != 0) {
		sys_log_erro("sys local mqtt init error\n");
		return -1;
	}

    ret = pthread_create(&thread_id_sys_check, NULL, pthread_sys_check, NULL);
    if (ret < 0) {
        sys_log_erro("pthread create fail\n");
        return -1;
    }
    return 0;
}
